package com.general.framework.core.utils;

import com.general.framework.core.exception.SystemErrorException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EncodingUtils;
import org.apache.http.util.EntityUtils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Map;

/**
 * @author leejean
 * @ClassName: HttpClientUtil
 * @Description: http请求工具类
 * @date 2019年11月17 12:52:20
 */
@Slf4j
public class HttpClientUtil {

    private static PoolingHttpClientConnectionManager cm;
    private static String EMPTY_STR = "";
    private static String UTF_8 = "UTF-8";

    private static void init() {
        if (cm == null) {
            cm = new PoolingHttpClientConnectionManager();
            // 整个连接池最大连接数
            cm.setMaxTotal(20);
            // 每路由最大连接数，默认值是2
            cm.setDefaultMaxPerRoute(5);
        }
    }

    /**
     * 通过连接池获取HttpClient
     *
     * @return
     */
    public static CloseableHttpClient getHttpClient() {
        init();
        return HttpClients.custom().setConnectionManager(cm).build();
    }

    /**
     * get请求
     *
     * @param url
     * @return
     */
    public static String httpGet(final String url) {
        final HttpGet httpGet = new HttpGet(url);
        return getResult(httpGet);
    }

    /**
     * get请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpGet(final String url, final Map<String, String> params) {
        final HttpGet httpGet = buildHttpGet(url, params);
        return getResult(httpGet);
    }

    /**
     * get请求
     *
     * @param url
     * @param headers
     * @param params
     * @return
     */
    public static String httpGet(final String url, final Map<String, String> headers, final Map<String, String> params) {
        final HttpGet httpGet = buildHttpGet(url, params);
        for (Map.Entry<String, String> param : headers.entrySet()) {
            httpGet.addHeader(param.getKey(), param.getValue());
        }
        return getResult(httpGet);
    }

    /**
     * post请求
     *
     * @param url
     * @return
     */
    public static String httpPost(final String url) {
        final HttpPost httpPost = new HttpPost(url);
        return getResult(httpPost);
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpPost(final String url, final String params) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(params, UTF_8));
        return getResult(httpPost);
    }

    /**
     * post请求
     *
     * @param url
     * @return
     */
    public static byte[] httpPostToByte(final String url, final String params) {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(params, UTF_8));
        return getResultToByte(httpPost);
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpPost(final String url, final Map<String, String> params) {
        return httpPost(url, null, params);
    }

    /**
     * post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String httpPost(final String url, final Map<String, String> headers, final Map<String, String> params) {
        final HttpPost httpPost = new HttpPost(url);
        final ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
        } catch (UnsupportedEncodingException e) {
            log.error("httpPostRequest参数编码异常", e);
            throw new SystemErrorException("Http request error");
        }
        if (headers != null && !headers.isEmpty()) {
            for (Map.Entry<String, String> param : headers.entrySet()) {
                httpPost.setHeader(param.getKey(), param.getValue());
            }
        }
        return getResult(httpPost);
    }

    /**
     * post请求
     *
     * @param url
     * @param headers
     * @param params
     * @return
     */
    public static String httpPost(final String url, final Map<String, String> headers, final String params) {
        final HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(params, UTF_8));
        for (Map.Entry<String, String> param : headers.entrySet()) {
            httpPost.addHeader(param.getKey(), param.getValue());
        }
        return getResult(httpPost);
    }

    public static String httpPostBody(final String url, final String jsonData) {
        final HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(jsonData, UTF_8));
        httpPost.addHeader("Content-type", "application/json");
        return getResult(httpPost);
    }

    /**
     * map数据转换
     *
     * @param params
     * @return
     */
    private static ArrayList<NameValuePair> covertParams2NVPS(final Map<String, String> params) {
        final ArrayList<NameValuePair> pairs = new ArrayList<>();
        for (Map.Entry<String, String> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), param.getValue()));
        }
        return pairs;
    }

    /**
     * 获取HttpGet实例
     *
     * @param url
     * @param params
     * @return
     * @throws URISyntaxException
     */
//    private static HttpGet buildHttpGet(final String url, final Map<String, String> params) {
//        final URIBuilder ub = new URIBuilder();
//        ub.setPath(url);
//        final ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
//        ub.setParameters(pairs);
//        try {
//            final HttpGet httpGet = new HttpGet(ub.build());
//            return httpGet;
//        } catch (URISyntaxException e) {
//            log.error("HttpClientUtil buildHttpGet URISyntaxException : ", e);
//            throw new SystemErrorException("Http request error");
//        }
//    }

    /**
     * 处理Http请求
     * <p>
     * setConnectTimeout：设置连接超时时间，单位毫秒。
     * setConnectionRequestTimeout：设置从connect Manager获取Connection 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
     * setSocketTimeout：请求获取数据的超时时间，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
     *
     * @param request
     * @return
     */
    private static String getResult(final HttpRequestBase request) {
        // 设置请求和传输超时时间
        final RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(60000)
                .setConnectionRequestTimeout(5000).setSocketTimeout(60000).build();
        request.setConfig(requestConfig);

        final CloseableHttpClient httpClient = getHttpClient();
        try {
            final CloseableHttpResponse response = httpClient.execute(request);
            final HttpEntity entity = response.getEntity();
            if (entity != null) {
                String result = EntityUtils.toString(entity, UTF_8);
                response.close();
                return result;
            }
        } catch (ClientProtocolException e) {
            log.error("HttpClientUtil ClientProtocolException : ", e);
            throw new SystemErrorException("Http request error");
        } catch (IOException e) {
            log.error("HttpClientUtil IOException : ", e);
            throw new SystemErrorException("Http request error");
        }
        return EMPTY_STR;
    }

    /**
     * 处理Http请求
     * <p>
     * setConnectTimeout：设置连接超时时间，单位毫秒。
     * setConnectionRequestTimeout：设置从connect Manager获取Connection 超时时间，单位毫秒。这个属性是新加的属性，因为目前版本是可以共享连接池的。
     * setSocketTimeout：请求获取数据的超时时间，单位毫秒。 如果访问一个接口，多少时间内无法返回数据，就直接放弃此次调用。
     *
     * @param request
     * @return
     */
    private static byte[] getResultToByte(final HttpRequestBase request) {
        // 设置请求和传输超时时间
        final RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(60000)
                .setConnectionRequestTimeout(5000).setSocketTimeout(60000).build();
        request.setConfig(requestConfig);

        final CloseableHttpClient httpClient = getHttpClient();
        try {
            final CloseableHttpResponse response = httpClient.execute(request);
            final HttpEntity entity = response.getEntity();
            byte[] data = null;
            if (entity != null) {
                InputStream inputStream = entity.getContent();
                data = readInputStream(inputStream);
                String resp = EncodingUtils.getString(data, "utf-8");
                if (resp.indexOf("JFIF") < 0) {
                    log.info("生成小程序二维码返回resp{}", resp);
                    return null;
                }
            }
            return data;
        } catch (ClientProtocolException e) {
            log.error("HttpClientUtil ClientProtocolException : ", e);
            throw new SystemErrorException("Http request error");
        } catch (IOException e) {
            log.error("HttpClientUtil IOException : ", e);
            throw new SystemErrorException("Http request error");
        } catch (Exception e) {
            throw new SystemErrorException("Http request error");
        }

    }

    /**
     * 将流 保存为数据数组
     *
     * @param inStream
     * @return
     * @throws Exception
     */
    public static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        // 创建一个Buffer字符串
        byte[] buffer = new byte[1024];
        // 每次读取的字符串长度，如果为-1，代表全部读取完毕
        int len = 0;
        // 使用一个输入流从buffer里把数据读取出来
        while ((len = inStream.read(buffer)) != -1) {
            // 用输出流往buffer里写入数据，中间参数代表从哪个位置开始读，len代表读取的长度
            outStream.write(buffer, 0, len);
        }
        // 关闭输入流
        inStream.close();
        // 把outStream里的数据写入内存
        return outStream.toByteArray();
    }

    /**
     * 构建带参数的HttpGet请求
     *
     * @param url 请求URL
     * @param params 请求参数
     * @return HttpGet对象
     */
    private static HttpGet buildHttpGet(final String url, final Map<String, String> params) {
        if (params == null || params.isEmpty()) {
            return new HttpGet(url);
        }
        
        StringBuilder urlBuilder = new StringBuilder(url);
        if (!url.contains("?")) {
            urlBuilder.append("?");
        } else {
            urlBuilder.append("&");
        }
        
        boolean first = url.endsWith("?");
        for (Map.Entry<String, String> entry : params.entrySet()) {
            if (!first) {
                urlBuilder.append("&");
            }
            try {
                urlBuilder.append(java.net.URLEncoder.encode(entry.getKey(), "UTF-8"))
                         .append("=")
                         .append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
            } catch (java.io.UnsupportedEncodingException e) {
                // UTF-8 should always be supported
                urlBuilder.append(entry.getKey()).append("=").append(entry.getValue());
            }
            first = false;
        }
        
        String finalUrl = urlBuilder.toString();
        log.info("🔗 构建的GET请求URL: {}", finalUrl);
        return new HttpGet(finalUrl);
    }

}
